package com.ddxcloud.movie.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.Month;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ddxcloud.common.constants.PayMethodConstant;
import com.ddxcloud.common.constants.ResultStatusConstant;
import com.ddxcloud.common.constants.enums.OrderStatusEnum;
import com.ddxcloud.common.handler.BusinessException;
import com.ddxcloud.movie.config.common.CommonProperties;
import com.ddxcloud.movie.config.common.JwtProperties;
import com.ddxcloud.movie.data.constants.ActivityTypeConstant;
import com.ddxcloud.movie.data.constants.CouponStatusConstant;
import com.ddxcloud.movie.data.constants.OrderTypeConstant;
import com.ddxcloud.movie.data.dto.*;
import com.ddxcloud.movie.data.entity.*;
import com.ddxcloud.movie.mapper.OrderMapper;
import com.ddxcloud.movie.mapper.PaymentOrderMapper;
import com.ddxcloud.movie.mapper.UserCouponMapper;
import com.ddxcloud.movie.service.*;
import com.ddxcloud.movie.data.vo.AddOrderVO;
import com.ddxcloud.movie.data.vo.OrderVO;
import com.ddxcloud.movie.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Resource
    UserService userService;

    @Resource
    MoviesService moviesService;

    @Resource
    OrderDetailService orderDetailService;

    @Resource
    PaymentOrderMapper paymentOrderMapper;

    @Resource
    AlipayService alipayService;

    @Resource
    WechatRefundService wechatRefundService;

    @Resource
    ChannelService channelService;

    @Resource
    CouponsService couponsService;

    @Resource
    UserCouponMapper userCouponMapper;

    @Resource
    CommonProperties commonProperties;

    @Resource
    JwtProperties jwtProperties;

    @Resource
    ActivityService activityService;

    @Override
    @Async("default")
    @Transactional(rollbackFor = Exception.class)
    public CompletableFuture<Order> createOrder(CreateOrderDTO createOrderDTO, HttpServletRequest servletRequest) {
        Order order = new Order();
        if (Objects.equals(createOrderDTO.getOrderType(), OrderTypeConstant.TYPE_0)) {
            MovieOrderDTO dto = createOrderDTO.getMovieOrder();
            order.setOrderType(OrderTypeConstant.TYPE_0);
            Jws<Claims> claimsJws = JwtUtil.parseJWT(servletRequest.getHeader(jwtProperties.getTokenName()));
            order.setUserId(Long.valueOf(claimsJws.getPayload().get("userId").toString()));
            String orderSn = "H5" + DateUtil.format(new Date(), "yyyyMMddHHmmss") + RandomUtil.randomNumbers(4);
            order.setOrderSn(orderSn);
            order.setCreateTime(new Date());
            order.setSpecialFlag(dto.getSpecialFlag());
            order.setPlatform(servletRequest.getHeader("platform"));
            boolean res = this.save(order);
            if (res) {
                AddOrderDTO addOrder = BeanUtil.copyProperties(dto, AddOrderDTO.class);
                addOrder.setOutTradeNo(order.getOrderSn());
                addOrder.setCouponCode(dto.getCouponCode());
                addOrder.setCallbackUrl(commonProperties.getDomainName() + "/order/notify");
                JSONObject jsonObject = moviesService.createOrder(addOrder, dto.getSpecialFlag());
                if (jsonObject.getInteger("code") == 0) {
                    try {
                        AddOrderVO vo = JSONUtil.toBean(jsonObject.getString("data"), AddOrderVO.class);
                        order.setAppId(vo.getAppId());
                        order.setOutTradeNo(vo.getOrderNo());
                        order.setOrderAmount(vo.getOrderAmount());
                        order.setOrderStatus(vo.getStatus());
                        // 存在使用了优惠券的情况下，记录优惠金额
                        if (dto.getCouponCode() != null && !dto.getCouponCode().isEmpty()) {
                            // 判断是否存在重复使用券码的情况
                            Integer isExist = baseMapper.isExistOrderByCouponCode(dto.getCouponCode(), order.getAppId());
                            if (isExist != null) {
                                throw new BusinessException("该优惠券已被使用，请勿重复使用");
                            }
                            JSONObject codeInfo = couponsService.getCodeInfo(dto.getCouponCode());
                            codeInfo = codeInfo.getJSONObject("data");
                            log.info("券码信息：{}", codeInfo);
                            if (codeInfo.containsKey("discountInfo")) {
                                codeInfo = codeInfo.getJSONObject("discountInfo");
                                order.setPreferentialAmount(codeInfo.getBigDecimal("discountFee"));
                                order.setCouponCode(dto.getCouponCode());
                            }
                        } else {
                            // 没有使用优惠券抵扣的情况下查看一下是否存在其他活动优惠
                            List<Activity> activities = activityService.getActivityByType(ActivityTypeConstant.TYPE_1);
                            for (Activity activity : activities) {
                                order.setRemark(activity.getActivityName() + "_" + activity.getId());
                                order.setPreferentialAmount(BigDecimal.valueOf(RandomUtil.randomDouble(0.5, Double.parseDouble(activity.getAttach()))));
                                order.setCouponCode(null); // 置空
                            }
                        }
                        res = this.updateById(order);
                        if (res) {
                            OrderDetail orderDetail = new OrderDetail();
                            orderDetail.setOrderId(order.getOrderId());
                            orderDetail.setCouponCode(dto.getCouponCode());
                            orderDetail.setMobile(dto.getMobile());
                            orderDetail.setFilmId(vo.getFilmId());
                            orderDetail.setFilmName(vo.getFilmName());
                            orderDetail.setFilmImg(dto.getFilmImg());
                            orderDetail.setCinemaId(dto.getCinemaId());
                            orderDetail.setCinemaName(dto.getCinemaName());
                            orderDetail.setCinemaAddress(dto.getCinemaAddress());
                            orderDetail.setChangeSeat(dto.getChangeSeat());
                            orderDetail.setShowId(dto.getShowId());
                            orderDetail.setSeats(vo.getSeats());
                            orderDetail.setDeliverMode(dto.getDeliverMode());
                            orderDetail.setStartTime(vo.getStartTime());
                            orderDetail.setEndTime(vo.getEndTime());
                            orderDetail.setSeatCount(dto.getSeatCount());
                            orderDetailService.save(orderDetail);
                        }
                    } catch (Exception e) {
                        log.error("createOrder errMsg: {}", e.getMessage());
                        throw new BusinessException("订单提交失败，请联系管理员");
                    }
                } else {
                    throw new BusinessException(jsonObject.getString("msg"));
                }
            }
        } else {
            GoodsOrderDTO dto = createOrderDTO.getGoodsOrder();
            order.setOrderType(OrderTypeConstant.TYPE_1);
            order.setUserId(userService.getUserByMobile(dto.getMobile()).getUserId());
            String orderSn = "G" + DateUtil.format(new Date(), "yyyyMMddHHmmss") + RandomUtil.randomNumbers(5);
            order.setOrderSn(orderSn);
            order.setOrderStatus(0);
            order.setCreateTime(new Date());
            order.setOrderAmount(dto.getGoodsAmount().multiply(new BigDecimal(dto.getGoodsNum())));
            order.setAppId(channelService.getChannel().getChannelId());
            boolean res = this.save(order);
            if (res) {
                OrderDetail orderDetail = new OrderDetail();
                orderDetail.setOrderId(order.getOrderId());
                orderDetail.setContacts(dto.getContacts());
                orderDetail.setMobile(dto.getMobile());
                orderDetail.setDeliveryAddress(dto.getDeliveryAddress());
                orderDetailService.save(orderDetail);
            }
        }
        return CompletableFuture.completedFuture(order);
    }

    @Override
    public Boolean confirmOrder(ConfirmOrderDTO dto) {
        Order order = baseMapper.getOrderByOrderSn(dto.getOrderNo());
        // 订单金额抵扣完仍大于0则代表此订单不满足免支付条件
        if (order.getOrderAmount().subtract(order.getPreferentialAmount()).compareTo(BigDecimal.ZERO) > 0) {
            throw new BusinessException("不满足免支付确认条件");
        }
        Channel channel = channelService.getById(order.getAppId());
        ConfirmOrderDTO confirmOrder = new ConfirmOrderDTO();
        confirmOrder.setOrderNo(order.getOutTradeNo());
        JSONObject result = moviesService.confirmOrder(confirmOrder, channel);
        log.info("confirmOrder request params: {}", JSONUtil.toJsonStr(result));
        if (result.containsKey("code") && result.containsKey("data")) {
            // 订单确认无论成功与否都执行修改订单状态和回填实际支付金额
            LambdaUpdateWrapper<Order> orderUpdate = new LambdaUpdateWrapper<>();
            orderUpdate.set(Order::getPayAmount, BigDecimal.ZERO);
            orderUpdate.set(Order::getOrderStatus, OrderStatusEnum.STATUS_1.getCode());
            orderUpdate.eq(Order::getOrderId, order.getOrderId());
            this.update(order, orderUpdate);
            if (result.getInteger("code") == 0) {
                // 判断是否有使用优惠券码
                if (ObjectUtil.isNotEmpty(order.getCouponCode())) {
                    userCouponMapper.updateUserCouponByCode(order.getCouponCode(), CouponStatusConstant.STATUS_1);
                }
            }
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    @Override
    public String notify(OrderDetailsDTO dto, HttpServletRequest request) {
        log.info("order notify: {}", JSONUtil.toJsonStr(dto));
        Order order = baseMapper.getOrderByOrderSn(dto.getOutTradeNo());
        if (ObjectUtil.isNotEmpty(order)) {
            if (Objects.equals(order.getOrderStatus(), dto.getStatus())
                    && !Objects.equals(dto.getStatus(), OrderStatusEnum.STATUS_2.getCode())
            ) {
                return ResultStatusConstant.SUCCESS;
            }
            if (Objects.equals(order.getOrderType(), OrderTypeConstant.TYPE_0)) {
                LambdaUpdateWrapper<Order> orderUpdate = new LambdaUpdateWrapper<>();
                orderUpdate.set(Order::getOrderStatus, dto.getStatus());
                orderUpdate.set(Order::getSettlementPrice, dto.getOrderAmount());
                orderUpdate.set(Order::getCommission, dto.getCommission());
                orderUpdate.eq(Order::getOrderId, order.getOrderId());
                baseMapper.update(order, orderUpdate);
                if (Objects.equals(dto.getStatus(), OrderStatusEnum.STATUS_2.getCode())) {
                    LambdaUpdateWrapper<OrderDetail> detailUpdate = new LambdaUpdateWrapper<>();
                    if (null != dto.getSeatsChange() && !dto.getSeatsChange().isEmpty()) {
                        detailUpdate.set(OrderDetail::getRemark, "原座位锁座失败，已替换为" + dto.getSeatsChange() + "；请按新座位入座观影。");
                    }
                    detailUpdate.set(OrderDetail::getCodes, dto.getCodes());
                    detailUpdate.eq(OrderDetail::getOrderId, order.getOrderId());
                    orderDetailService.update(detailUpdate);
                } else if (Objects.equals(dto.getStatus(), OrderStatusEnum.STATUS_4.getCode())) {
                    PaymentOrder paymentOrder = paymentOrderMapper.getOrderByOrderId(order.getOrderId().toString());
                    if (ObjectUtil.isNotEmpty(paymentOrder) && paymentOrder.getPayStatus() == 1) {
                        RefundDTO refund = new RefundDTO();
                        refund.setOrderId(order.getOrderId().toString());
                        refund.setRefundAmount(paymentOrder.getPayAmount());
                        refund.setReason("出票失败或其他售后问题申请退款");
                        refund.setOrderStatus(dto.getStatus());
                        if (Objects.equals(paymentOrder.getPayMethod(), PayMethodConstant.ALIPAY)) {
                            alipayService.refund(refund, request);
                        } else if (Objects.equals(paymentOrder.getPayMethod(), PayMethodConstant.WECHAT)) {
                            wechatRefundService.createOrder(refund, request);
                        }
                        // 判断是否使用了优惠券，需要归还券码
                        if (ObjectUtil.isNotEmpty(order.getCouponCode())) {
                            userCouponMapper.updateUserCouponByCode(order.getCouponCode(), CouponStatusConstant.STATUS_0);
                        }
                    }
                } else if (Objects.equals(dto.getStatus(), OrderStatusEnum.STATUS_5.getCode())) {
                    log.info("需售后订单：{}", dto.getOutTradeNo());
                }
            }
            return ResultStatusConstant.SUCCESS;
        }
        return ResultStatusConstant.FAIL;
    }

    @Override
    public IPage<OrderVO> getOrderList(PageDTO dto) {
        Page<OrderVO> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        dto.setChannelId(channelService.getChannel().getChannelId());
        return baseMapper.getOrderList(page, dto);
    }

    @Override
    public OrderVO getOrderInfo(Long orderId) {
        return baseMapper.getOrderInfo(orderId);
    }

    @Override
    public Boolean updateOrderForPaySuccess(String orderId, PaymentOrder paymentOrder, HttpServletRequest request) {
        Order order = this.getById(orderId);
        if (ObjectUtil.isNotEmpty(order)) {
            Channel channel = channelService.getById(order.getAppId());
            ConfirmOrderDTO confirmOrder = new ConfirmOrderDTO();
            confirmOrder.setOrderNo(order.getOutTradeNo());
            JSONObject result = moviesService.confirmOrder(confirmOrder, channel);
            if (result.containsKey("code") && result.containsKey("data")) {
                // 订单确认无论成功与否都执行修改订单状态和回填实际支付金额
                LambdaUpdateWrapper<Order> orderUpdate = new LambdaUpdateWrapper<>();
                orderUpdate.set(Order::getPayAmount, new BigDecimal(paymentOrder.getPayAmount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
                orderUpdate.set(Order::getOrderStatus, OrderStatusEnum.STATUS_1.getCode());
                orderUpdate.eq(Order::getOrderId, order.getOrderId());
                this.update(order, orderUpdate);
                // 订单确认失败时立即执行退款操作
                if (result.getInteger("code") == 1) {
                    RefundDTO refund = new RefundDTO();
                    refund.setOrderId(orderId);
                    refund.setRefundAmount(paymentOrder.getPayAmount());
                    refund.setReason(result.getString("msg"));
                    refund.setOrderStatus(OrderStatusEnum.STATUS_4.getCode());
                    switch (paymentOrder.getPayMethod()) {
                        case 1: // 支付宝支付
                            alipayService.refund(refund, request);
                            break;
                        case 2: // 微信支付
                            wechatRefundService.createOrder(refund, request);
                            break;
                    }
                } else {
                    // 判断是否有使用优惠券码
                    if (ObjectUtil.isNotEmpty(order.getCouponCode())) {
                        userCouponMapper.updateUserCouponByCode(order.getCouponCode(), CouponStatusConstant.STATUS_1);
                    }
                }
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }
}
